package com.cloudkinto.service.address.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.bo.address.AddressBookAddBo;
import com.cloudkinto.bo.address.AddressBookQueryBo;
import com.cloudkinto.bo.address.AddressBookUpdateBo;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.utils.StringsUtil;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.AddressBookDao;
import com.cloudkinto.dto.address.AddressBookImportDto;
import com.cloudkinto.entity.AddressBookDo;
import com.cloudkinto.enumType.address.AddressBookTypeEnum;
import com.cloudkinto.enumType.common.CountryEnum;
import com.cloudkinto.service.address.AddressBookService;
import com.cloudkinto.vo.common.CommonBatchOperationVo;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 地址簿 service实现类
 * </p>
 *
 * @author long
 * @since 2025-01-06
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class AddressBookServiceImpl extends ServiceImpl<AddressBookDao, AddressBookDo> implements AddressBookService {

    @Override
    public int add(AddressBookAddBo req) {
        AddressBookDo entityDo = new AddressBookDo();
        BeanUtils.copyProperties(req, entityDo);
        return baseMapper.insert(entityDo);
    }

    @Override
    public int update(AddressBookUpdateBo req) {
        AddressBookDo entityDo = baseMapper.selectById(req.getId());
        BeanUtils.copyProperties(req, entityDo);
        return baseMapper.updateById(entityDo);
    }

    @Override
    public int delete(Long id) {
        return baseMapper.deleteById(id);
    }

    @Override
    public AddressBookDo detail(Long id) {
        return baseMapper.selectById(id);
    }


    @Override
    public SingleResult pageInit(Long tenantId, Long companyId) {
        HashMap<String, Object> map = new HashMap<>(2);
        map.put("addressBookTypeList", AddressBookTypeEnum.getAddressBookTypeList());
        map.put("companyTagList", this.listObjs(Wrappers.<AddressBookDo>lambdaQuery().select(AddressBookDo::getCompanyTag).eq(Objects.nonNull(tenantId), AddressBookDo::getTenantId, tenantId).eq(Objects.nonNull(companyId), AddressBookDo::getCompanyId, companyId).isNotNull(AddressBookDo::getCompanyTag), ObjectUtil::toString).stream().filter(StrUtil::isNotBlank).collect(Collectors.toSet()));
        return SingleResult.success(map);
    }

    @Override
    public PageResult getListPage(AddressBookQueryBo bo) {
        IPage<AddressBookDo> page = new Page<>(bo.getCurrent(), bo.getPageSize());
        page = this.page(page, this.queryBuild(bo));
        return new PageResult(page.getRecords(), (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
    }

    @Override
    public SingleResult listPageStatusSta(AddressBookQueryBo bo) {
        List<AddressBookDo> list = this.list(queryBuild(bo));
        Map<Integer, List<AddressBookDo>> typeMap = list.stream().collect(Collectors.groupingBy(AddressBookDo::getAddressType));
        Map<Integer, Integer> map = new HashMap<>(8);
        for (AddressBookTypeEnum typeEnum : AddressBookTypeEnum.values()) {
            map.put(typeEnum.getValue(), typeMap.getOrDefault(typeEnum.getValue(), Collections.emptyList()).size());
        }
        return SingleResult.success(map);
    }

    @Override
    public void getAddressBookImportTemplate(HttpServletResponse response) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/Address_Book.xlsx", true);
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(2);
        //处理第一个sheet(CountryCodeOfOrigin原产地简码)
        List<Map<String, String>> countryEnumList = CountryEnum.getCountryEnumList();
        sheetMap.put("countryList", countryEnumList);
        //处理第二个sheet(Address type/地址类型)
        List<Map<String, String>> addressBookEnumList = AddressBookTypeEnum.getAddressBookEnumList();
        sheetMap.put("addressBookType", addressBookEnumList);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=Address_Book.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, sheetMap)) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public CommonBatchOperationVo importAdd(MultipartFile file) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<AddressBookImportDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, AddressBookImportDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        AtomicInteger rowNo = new AtomicInteger(1);
        importDtoList.forEach(i -> i.setRowNo(rowNo.getAndIncrement()));
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        List<Integer> exceptionRowNoList = importDtoList.stream().filter(
                i -> StrUtil.isBlank(i.getName())
                     || StrUtil.isBlank(i.getAddressType())
                     || StrUtil.isBlank(i.getReceiver())
                     || StrUtil.isBlank(i.getCountryRegionCode())
                     || StrUtil.isBlank(i.getProvinceName())
//                     || StrUtil.isBlank(i.getCityName())
                     || StrUtil.isBlank(i.getPostCode())
                     || StrUtil.isBlank(i.getAddress1())
        ).map(AddressBookImportDto::getRowNo).collect(Collectors.toList());
        importDtoList = importDtoList.stream().filter(i -> !exceptionRowNoList.contains(i.getRowNo())).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", exceptionRowNoList.stream().map(String::valueOf).collect(Collectors.joining(",")));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        List<AddressBookDo> entityList = new ArrayList<>();
        for (AddressBookImportDto dto : importDtoList) {
            AddressBookDo entityDo = new AddressBookDo();
            BeanUtils.copyProperties(dto, entityDo);
            if (AddressBookTypeEnum.getValueByName(dto.getAddressType()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getRowNo());
                map.put("errorMsg", "Address type/地址类型[" + dto.getAddressType() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (!CountryEnum.getCountryList().contains(dto.getCountryRegionCode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getRowNo());
                map.put("errorMsg", "Country/国家/地区 Wrong,Please select from the drop-down list");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            entityDo.setAddressType(AddressBookTypeEnum.getValueByName(dto.getAddressType()));
            entityList.add(entityDo);
        }
        this.saveBatch(entityList);
        return res;
    }

    @Override
    public Long checkExist(AddressBookAddBo bo, Long tenantId, Long companyId) {
        AddressBookDo addressBookDo = baseMapper.selectOne(
                Wrappers.<AddressBookDo>lambdaQuery()
                        .eq(AddressBookDo::getTenantId, tenantId)
                        .eq(AddressBookDo::getCompanyId, companyId)
                        .eq(AddressBookDo::getCompanyTag, bo.getCompanyTag())
//                        .eq(AddressBookDo::getName, bo.getName())
                        .eq(AddressBookDo::getAddressType, bo.getAddressType())
                        .eq(AddressBookDo::getReceiver, bo.getReceiver())
                        .eq(AddressBookDo::getPhone, bo.getPhone())
                        .eq(AddressBookDo::getEmail, bo.getEmail())
                        .eq(AddressBookDo::getCompanyName, bo.getCompanyName())
                        .eq(AddressBookDo::getCountryRegionCode, bo.getCountryRegionCode())
                        .eq(AddressBookDo::getProvinceName, bo.getProvinceName())
                        .eq(AddressBookDo::getCityName, bo.getCityName())
                        .eq(AddressBookDo::getPostCode, bo.getPostCode())
                        .eq(AddressBookDo::getHouseNum, bo.getHouseNum())
                        .eq(AddressBookDo::getAddress1, bo.getAddress1())
                        .eq(AddressBookDo::getAddress2, bo.getAddress2())
        );
        if (Objects.nonNull(addressBookDo)) {
            return addressBookDo.getId();
        }
        AddressBookDo temp = new AddressBookDo();
        BeanUtils.copyProperties(bo, temp);
        baseMapper.insert(temp);
        return temp.getId();
    }

    private LambdaQueryWrapper<AddressBookDo> queryBuild(AddressBookQueryBo bo) {
        LambdaQueryWrapper<AddressBookDo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(AddressBookDo::getTenantId, bo.getTenantId());
        lqw.eq(AddressBookDo::getCompanyId, bo.getCompanyId());
        lqw.eq(StrUtil.isNotBlank(bo.getCompanyTag()), AddressBookDo::getCompanyTag, bo.getCompanyTag());
        lqw.like(StrUtil.isNotBlank(bo.getName()), AddressBookDo::getName, bo.getName());
        lqw.eq(Objects.nonNull(bo.getAddressType()), AddressBookDo::getAddressType, bo.getAddressType());
        lqw.eq(StrUtil.isNotBlank(bo.getCountryRegionCode()), AddressBookDo::getCountryRegionCode, bo.getCountryRegionCode());
        lqw.like(StrUtil.isNotBlank(bo.getProvinceName()), AddressBookDo::getProvinceName, bo.getProvinceName());
        lqw.like(StrUtil.isNotBlank(bo.getCityName()), AddressBookDo::getCityName, bo.getCityName());
        lqw.eq(StrUtil.isNotBlank(bo.getPostCode()), AddressBookDo::getPostCode, bo.getPostCode());
        lqw.like(StrUtil.isNotBlank(bo.getReceiver()), AddressBookDo::getReceiver, bo.getReceiver());
        lqw.like(StrUtil.isNotBlank(bo.getPhone()), AddressBookDo::getPhone, bo.getPhone());
        lqw.like(StrUtil.isNotBlank(bo.getEmail()), AddressBookDo::getEmail, bo.getEmail());
        return lqw;
    }
}
